import struct
import serial
import time
import json


class serialFlashLoader:
    def __init__(self, COMx: str, baudrate=115200, printf=print):
        self.port = COMx
        self.printf = printf
        self.baudrate = baudrate
        self.serial = serial.Serial(self.port, self.baudrate)
        try:
            self.serial.close()
            print("已关闭串口")
        except:
            print("无需关闭串口")
            pass
        self.error_num = 0

    def openSerial(self):
        status = True
        try:
            self.serial.open()
        except:
            print("串口开启失败")
            status = False
        return status


    def send_u8str(self, u8str):
        self.openSerial()
        print("开始发送......",u8str)
        self.serial.write(u8str)
        time.sleep(0.5)
        rx = self.serial.read(self.serial.in_waiting)
        try:
            print(rx.decode('gb2312'))
            self.serial.close()
            return rx.decode('gb2312')
        except:
            print("接收到错误字节码", rx)
            self.serial.close()
            return False

    def get_pageNum(self, asciilist: list):
        length = len(asciilist)
        print(f"待发送内容总长度为{length}")
        if length % 1024 == 0:
            page_num = int(length / 1024)
        else:
            page_num = int(length / 1024) + 1
        return page_num

    def sendAsciiList(self, asciilist: list):
        status = True
        for i in asciilist:
            if i > 255 or i < 0:
                status = False
                break
        if status:
            self.serial.open()
            page_num = self.get_pageNum(asciilist)

            for i in range(page_num):
                while True:
                    if self.sendpage(i, asciilist,page_num):
                        break
            print("发送完成！即将关闭串口......")
            self.printf(">>> 发送完成！\n")
            self.serial.close()
        else:
            print("该列表不是ASCII列表，请重新输入！")

    def cutList2Page(self, page: int, tx_list: list):
        if (1 + page) * 1024 > len(tx_list):
            return tx_list[page * 1024:]
        else:
            return tx_list[page * 1024:(1 + page) * 1024]

    def listToBytes(self, arr: list) -> bytes:
        """
        使用struct.pack
        将一个列表转换为bytes
        :param arr: list of int 0 - 255
        :return:
        """
        return struct.pack("B" * len(arr), *arr)

    def sendpage(self, page: int, tx_str: list,page_num):
        page_data = self.cutList2Page(page,tx_str)
        length = len(page_data)
        len_l = (length % 256).to_bytes(1,"big")
        len_h = (length // 256).to_bytes(1,"big")
        page_l = (page % 256).to_bytes(1,"big")
        page_h = (page // 256).to_bytes(1,"big")

        print(f"即将发送第{page}页内容(长度:{length})")
        self.printf(f">>> 即将发送第{page+1}/{page_num}页内容(长度:{length})")

        head = b'@'+page_h+page_l+b'='+len_h+len_l+b'$'
        print(head)
        print("开始发送......")

        self.serial.write(head + self.listToBytes(page_data) + b"#\r\n")
        time.sleep(0.5)

        print(f"第{page}页发送完成! 等待单片机保存......")
        self.printf(f"第{page+1}/{page_num}页发送完成! 等待单片机保存...\n")
        rx = self.serial.read(self.serial.in_waiting)
        try:
            rx.decode('gb2312')
        except:
            print("接收到错误字节码", rx)
            return False

        if "SPI_FLASH" in rx.decode('gb2312'):
            print(rx.decode('gb2312'))
            return True
        else:
            print("出现错误",rx.decode('gb2312'))
            return False

    def readAsciiList_Page(self, page, txlist: list):
        page_list = list(range(self.get_pageNum(txlist)))
        print(page_list[-1])
        toRead = self.cutList2Page(page_list[page], txlist)
        line = 0
        rn = 0
        print("\n%4d:  " % line, end='')
        line += 1
        for i in toRead:
            print(f"{hex(i)}, ", end='')
            rn += 1
            if rn == 16:
                rn = 0
                print("\n%4d:  " % line, end='')
                line += 1


class RGB565_Picture:
    def __init__(self, width: int, height: int, address: str):
        self.width = width
        self.height = height
        self.address = address
        self.pxel = []
        self.byte = []
        self.bytearray = bytearray()
        self.byts_string = ""

        with open(self.address, 'r+', encoding='utf-8') as f:
            pxel_line = f.readlines()[1:]
            pass

        pxel_line[-1] = pxel_line[-1].split('}')[0] + ',\n'
        pxel = list()

        for line in pxel_line:
            pxel += line[0:-2].split(',')

        for i in range(len(pxel)):
            if (len(pxel[i]) > 0):
                b = (int(pxel[i], 16))
                self.byte.append(b)
                self.bytearray.append(b)
                self.byts_string += str(chr(b))

        self.save()

    def save(self):
        with open(self.address.split('.')[0] + '_u8.json', 'w+') as f:
            json.dump(self.byte, f)

    def load(self):
        with open(self.address.split('.')[0] + '.json', 'r+') as f:
            js = json.load(f)
            self.width = js[0]
            self.height = js[1]
            self.pxel = js[2:]

def scanSerial():
    import serial.tools.list_ports
    import re
    ports = list(serial.tools.list_ports.comports())
    r=re.compile(r'COM\d{1,3}')
    s = []
    for i in ports:
        s.append(re.match(r,str(ports[0])).group())
    return s

if __name__ == '__main__':
    # lcd_pic = RGB565_Picture(319, 240, 'skadi.h')
    # ser = serialFlashLoader('COM6', 115200)
    # ser.sendAsciiList(lcd_pic.byte)
    pass
    # ser.openSerial()
    # ser.sendpage(129,chen.byte)
    # ser.readAsciiList_Page(128,chen.byte)

    # assert len(COMs) > 0, "No COMs found"
    # COM = COMs[0]
    # ser = openCom(COM)
    # ser.write(b + b"\r\n")
    #
    # import time
    # time.sleep(1)
    # # Read all data
    # while ser.in_waiting:
    #     print(ser.read(ser.in_waiting))
    # ser.close()
